package com.sheepshead.models;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;

import org.apache.log4j.Logger;

import com.sheepshead.enums.CardEnum;
import com.sheepshead.enums.SuitEnum;

public class Player {
	private List<Card> hand;
	private boolean picked;
	private boolean partner;
	private int handPointValue;
	private int position;
	private boolean alone;
	private SuitEnum calledSuit;
	private List<Card> tricks;
	private boolean knowPartner;
	
	public boolean isKnowPartner() {
		return knowPartner;
	}
	public void setKnowPartner(boolean knowPartner) {
		this.knowPartner = knowPartner;
	}
	public List<Card> getTricks() {
		return tricks;
	}
	public void setTricks(List<Card> tricks) {
		this.tricks = tricks;
	}

	private static final Logger logger = Logger.getLogger(Player.class);
	
	public SuitEnum getCalledSuit() {
		return calledSuit;
	}
	public void setCalledSuit(SuitEnum calledSuit) {
		this.calledSuit = calledSuit;
	}
	public boolean isAlone() {
		return alone;
	}
	public void setAlone(boolean alone) {
		this.alone = alone;
	}
	public Player(int position)
	{
		this.position = position;
		this.tricks = new ArrayList<Card>();
	}	
	public int getHandPointValue() {
		return handPointValue;
	}
	public int getPosition() {
		return position;
	}

	public void setPosition(int position) {
		this.position = position;
	}
	public void setHandPointValue(int handPointValue) {
		this.handPointValue = handPointValue;
	}
	public List<Card> getHand() {
		return hand;
	}

	public void setHand(List<Card> hand) {
		this.hand = hand;
	}
	public boolean isPicked() {
		return picked;
	}
	public void setPicked(boolean picked) {
		this.picked = picked;
	}
	public boolean isPartner() {
		return partner;
	}
	public void setPartner(boolean partner) {
		this.partner = partner;
	}

	public static int calculatePointValue(List<Card> hand) {
		int points = 0;
		for(Card card : hand)
		{
			if(card.getValue().equals(CardEnum.QUEEN))
			{
				if(card.getSuit().equals(SuitEnum.CLUBS) || card.getSuit().equals(SuitEnum.SPADES))
				{
					points += 4;
				}
				else
				{
					points +=3;
				}
			}
			else if((card.getValue().equals(CardEnum.TEN) || card.getValue().equals(CardEnum.ACE)) && !card.isTrump())
			{
				points += 1;
			}
			else if(card.isTrump())
			{
				points += 2;
			}
		}
		return points;
	}

	public List<Card> chooseDiscards() {
		List<Card> discards = new ArrayList<Card>();
		List<Card> aces = new ArrayList<Card>();
		if(logger.isDebugEnabled())
		{
			logger.debug("Player " + position + " picked.\n");
		}
		discards = findDiscards(discards);
		if(logger.isDebugEnabled())
		{
			for(Card discard : discards)
			{
				logger.debug("Player discarded : " + discard.getCardName() + ".\n");	
			}
			
		}
		boolean hasAce = false;
		hand.removeAll(discards);
		
		// Seperate out aces to make it easier to call a suit
		for(Card card : hand)
		{
			if(card.getValue().equals(CardEnum.ACE))
			{
				aces.add(card);
			}
		}
		// Make sure the ace isn't in the discard pile
		for(Card discard : discards)
		{
			if(discard.getValue().equals(CardEnum.ACE))
			{
				aces.add(discard);
			}
		}
		for(Card card : hand)
		{
			if(!card.isTrump())
			{
				if(aces.size() == 0)
				{
					setCalledSuit(card.getSuit());
					break;
				}
				else
				{
					for(Card ace : aces)
					{
						if(ace.getSuit().equals(card.getSuit()))
						{
							hasAce = true;
							break;
						}
					}
					if(!hasAce)
					{
						setCalledSuit(card.getSuit());
						break;						
					}
					else
					{
						hasAce = false;
					}
				}
				hasAce = false;
			}
		}
		handPointValue = calculatePointValue(hand);
		if(calledSuit == null || handPointValue >= 13)
		{
			alone = true;
		}
		if(logger.isDebugEnabled())
		{
			String finalHand = "";
			for(int i=0; i < hand.size(); i++)
			{
				finalHand += hand.get(i).getCardName();
				if(i != hand.size() - 1)
				{
					finalHand += ",";
				}
			}
			logger.debug("Final hand: " + finalHand + "\n");
			if(alone)
			{
				logger.debug("Player is going alone.\n");
			}
			else
			{
				logger.debug("Player calls: " + calledSuit + "\n");
			}
		}
		return discards;		
	}
	
	private List<Card> findDiscards(List<Card> discards) {
		HashMap<SuitEnum, List<Card>> discardMap = new HashMap<SuitEnum, List<Card>>();
		int numDiscards = 0;
		// Check for short suited
		for(Card card : hand)
		{
			if(!card.isTrump())
			{
				if(discardMap.get(card.getSuit()) == null)
				{
					List<Card> cards = new ArrayList<Card>();
					cards.add(card);
					discardMap.put(card.getSuit(), cards);
				}
				else
				{
					List<Card> cards = discardMap.get(card.getSuit());
					cards.add(card);
				}
			}
		}
		// Check for points, aces first
		for(Card card : hand)
		{
			if(card.getValue().equals(CardEnum.ACE) && !card.isTrump())
			{
				discards.add(card);
				++numDiscards;
				if(numDiscards == 2)
				{
					return discards;
				}
			}
		}
		// Check for 2 of a suit first, better to get rid of a long suit
		for(SuitEnum suit : discardMap.keySet())
		{
			if(discardMap.get(suit).size() == 2 && numDiscards == 0)
			{
				discards.addAll(discardMap.get(suit));
				return discards;
			}
		}
		// Check for 1 of a suit
		for(SuitEnum suit : discardMap.keySet())
		{
			if(discardMap.get(suit).size() == 1)
			{
				discards.addAll(discardMap.get(suit));
				++numDiscards;
				if(numDiscards == 2)
				{
					return discards;
				}
			}
		}
		// Check for tens
		for(Card card : hand)
		{
			if(card.getValue().equals(CardEnum.TEN) && !card.isTrump())
			{
				discards.add(card);
				++numDiscards;
				if(numDiscards == 2)
				{
					return discards;
				}
			}
		}
		// Check for Kings
		for(Card card : hand)
		{
			if(card.getValue().equals(CardEnum.KING) && !card.isTrump())
			{
				discards.add(card);
				++numDiscards;
				if(numDiscards == 2)
				{
					return discards;
				}
			}
		}
		// No points, no short suit, find first cards in hand to discard
		for(Card card : hand)
		{
			if(!card.isTrump())
			{
				discards.add(card);
				++numDiscards;
				if(numDiscards == 2)
				{
					return discards;
				}
			}
		}
		// Rest of, or entire hand is trump, so get rid of trump
		if(numDiscards < 2)
		{
			// Sort then reverse the order to make taking low trump easier
			Collections.sort(hand);
			Collections.reverse(hand);
			discards.add(hand.get(0));
			++numDiscards;
			if(numDiscards == 2)
			{
				return discards;
			}
			else
			{
				discards.add(hand.get(1));
			}
		}
		return discards;
	}
	
	public void printHand()
	{
		System.out.println("Current hand:\n------------");
		for(int i = 1; i<=hand.size(); i++)
		{
			System.out.println(i + ". " + hand.get(i-1).getCardName());
		}
	}
	public Card chooseCardToPlay(SuitEnum calledSuit, SuitEnum leadSuit, List<Player> players, List<Card>currentPile) {
		//TODO: Finish up the code for choosing a card to play
		return null;
	}
	
	public Card chooseCardToLead(SuitEnum calledSuit) {
		Collections.sort(hand);
		// if player is parter or picked try to lead with trump or points first, then fail off
		if(partner || picked)
		{
			for(Card card : hand)
			{
				if(card.isTrump())
				{
					return card;
				}
				else if(card.getPointValue() > 0 && !card.getSuit().equals(calledSuit))
				{
					return card;
				}
			}
			// if no cards to play that are trump or points, fail off non-called suit
			Collections.reverse(hand);
			for(Card card : hand)
			{
				if(!card.getSuit().equals(calledSuit))
				{
					return card;
				}
			}
			// if you can't play a fail card, play lowest called suit, but if partner, play Ace of called suit
			if(partner)
			{
				for(Card card : hand)
				{
					if(card.getValue().equals(CardEnum.ACE))
					{
						return card;
					}
				}
			}
			else
			{
				return hand.get(0);
			}
		}
		// Logic for non-partner / picker
		else
		{
			// If called suit has not been lead, try to lead with called suit
			if(!knowPartner)
			{
				for(Card card : hand)
				{
					if(card.getSuit().equals(calledSuit))
					{
						return card;
					}
				}
			}
		}
		// Otherwise fail off
		Collections.reverse(hand);
		return hand.get(0);
	}
}
